import json

from django import http
from django.shortcuts import render, redirect
from django.urls import reverse
from django.views import View
from django_redis import get_redis_connection

from .utils import *
from goods.models import SKU


class CartManager(View):
    """
    购物车管理
    """
    sku_id = 0
    count = 0
    selected = True
    sku = None

    def parse(self, request):
        """
        解析json数据
        :param request: 请求体
        """
        # print(request.body)
        # print(request.META)
        data = json.loads(request.body.decode())
        self.sku_id = data.get('sku_id', None)
        self.count = data.get('count', None)
        self.selected = data.get('selected', True)

    def check_paras(self):
        """
        校验参数
        :return: 有错：响应 没错：None
        """
        if not all([self.sku_id, self.count]):
            return http.JsonResponse({'code': 4011, 'errmsg': '缺少参数'})
        try:
            # print(type(self.count))    # int
            self.count = int(self.count)
        except:
            return http.JsonResponse({'code': 4012, 'errmsg': '错误的count'})

        try:
            # print(type(self.sku_id))   # int
            self.sku_id = int(self.sku_id)
            self.sku = SKU.objects.get(id=self.sku_id)
        except:
            return http.JsonResponse({'code': 4013, 'errmsg': '错误的sku_id'})

        if not isinstance(self.selected, bool):
            return http.JsonResponse({'code': 4014, 'errmsg': '错误的selected'})

        return None

    def set_redis(self):
        """
        设置redis中的数据
        :return:
        """
        user = self.request.user
        method = self.request.method
        # 连接redis并创建管道
        redis_conn = get_redis_connection('cart')
        pl = redis_conn.pipeline()
        # 保存数据，
        # 数据结构： hash字典 cart_id:{sku_id: count, ...}
        if method == 'POST':
            # 添加商品，使用hash字典的增量命令
            pl.hincrby('cart_%d' % user.id, self.sku_id, self.count)
        elif method == 'PUT':
            # 修改数量
            pl.hset('cart_%s' % user.id, self.sku_id, self.count)
        elif method == 'DELETE':
            # 删除商品：
            pl.hdel('cart_%s' % user.id, self.sku_id)
            self.selected = False

        # 无序集合,保存已勾选商品 selected_id:[sku_id,...]
        if self.selected:
            pl.sadd('selected_%s' % user.id, self.sku_id)
        else:
            pl.srem('selected_%s' % user.id, self.sku_id)
        pl.execute()

        return

    def set_cookies(self, rsp):
        """
        设置cookie中购物车数据
        :return: 响应
        """
        method = self.request.method

        # 获取cookie原购物车数据
        cart = self.request.COOKIES.get('cart')

        # 解密
        cart = decrypt(cart)
        if not isinstance(cart, dict):
            cart = {}
      # print(cart)

        # 保存数据
        # 数据结构：cart:{sku_id:{count:int, selected:bool},...}
        if method == 'PUT':
            # 修改购物车商品信息
            cart[self.sku_id] = {
                'count': self.count,
                'selected': self.selected
            }
        elif method == 'POST':
            # 添加商品或增加数量
            if self.sku_id in cart:
                cart[self.sku_id]['count'] += self.count
                cart[self.sku_id]['selected'] = self.selected
            else:
                cart[self.sku_id] = {'count': self.count, 'selected': self.selected}
        elif method == 'DELETE':
            if self.sku_id in cart:
                cart.pop(self.sku_id)

      # print(cart)

        # 加密
        cart = encrypt(cart)

        # 设置响应cookies
        rsp.set_cookie('cart', cart)  # 关闭浏览器时清除

        return rsp

    def post(self, request):
        """
        添加商品
        :param request: post /carts/
            json数据: {sku_id(商品)：int, count(数量)：int, [selected(勾选): bool]}
        :return: json {code: int, errmsg: 信息}
        """
        # 解析json数据
        self.parse(request)
        # 校验数据
        rsp = self.check_paras()
        if rsp:
            return rsp

        rsp = http.JsonResponse({'code': 0, 'errmsg': '添加成功'})

        # 判断用户是否登录
        if request.user and request.user.is_authenticated:
            # 登录用户，保存数据到redis
            self.set_redis()
            # 响应
            return rsp
        else:
            # 未登录用户，保存数据到cookies
            return self.set_cookies(rsp)

    def get(self, request):
        """
        展示购物车
        :param request: get /carts/
        :return: cart.html
        """
        # 默认不显示更新购物车按钮，只有登录用户的cookie中有购物记录才显示
        update_cart = False
        # 检查cookies中是否有购物车记录
        # 获取cookies并解密
        data = request.COOKIES.get('cart')
        data = decrypt(data)
        if not isinstance(data, dict):
            data = {}

        # 判断用户是否登录
        user = request.user
        if user and user.is_authenticated:
            # 登录用户显示redis中的购物车记录，如果cookie中有记录就显示更新购物车按钮。
            # 显示更新购物车按钮
            if data:
                update_cart = True
            # 连接 redis
            redis_conn = get_redis_connection('cart')
            # 取出数据
            cart = redis_conn.hgetall('cart_%s' % user.id)
            selected = redis_conn.smembers('selected_%s' % user.id)
            data = {}
            for sku, count in cart.items():
                data[int(sku)] = {
                    'count': int(count),
                    'selected': sku in selected,
                }
            # print(type(cart), cart)
            # print(type(selected), selected)
            # print(data)

        # 未登录用户直接使用cookies。

        # 构造响应数据
        # 获取字典中所有的key,(sku_id)
        sku_ids = data.keys()
        # 一次性查询出所有的skus
        skus = SKU.objects.filter(id__in=sku_ids)

        cart_skus = []
        for sku in skus:
            cart_skus.append({
                'id': sku.id,
                'count': data.get(sku.id).get('count'),
                'selected': str(data.get(sku.id).get('selected')), # 将 True，转'True'，方便json解析
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'detail_url': reverse('goods:detail', args=(sku.id,)),
                'price': str(sku.price),  # 从Decimal('10.2')中取出'10.2'，方便json解析
                'amount': str(sku.price * data.get(sku.id).get('count'))
            })
        # print(cart_skus)
        data = {
            'cart_skus': cart_skus,
            'update_cart': update_cart
        }

        # 响应渲染模板
        return render(request, 'cart.html', data)

    def put(self, request):
        """
        修改购物车
        :param request: put /carts/
            json数据: {sku_id(商品)：int, count(数量)：int, [selected(勾选): bool]}
        :return: json数据: {sku_id(商品)：int, count(数量)：int, [selected(勾选): bool]}
        """
        # 解析json数据
        self.parse(request)
        # 校验数据
        rsp = self.check_paras()
        if rsp:
            return rsp
        # 创建响应对象
        cart_sku = {
            'id': self.sku_id,
            'count': self.count,
            'selected': self.selected,
            'name': self.sku.name,
            'default_image_url': self.sku.default_image.url,
            'price': self.sku.price,
            'amount': self.sku.price * self.count
        }
        rsp = http.JsonResponse({'code': 0, 'errmsg': '修改购物车成功',
                                 'cart_sku': cart_sku})

        # 判断用户是否登录
        user = request.user
        if user.is_authenticated:
            # 登录用户，修改redis数据库
            self.set_redis()
            # 响应
            return rsp
        else:
            # 未登录用户，修改cookies
            return self.set_cookies(rsp)

    def delete(self, request):
        """
        删除商品
        :param request: delete /carts/  json:{sku_id:int}
        :return: json {code:int, errmsg: str}
        """
        # 解析json数据
        self.parse(request)
        # 校验数据
        if not SKU.objects.filter(id=self.sku_id):
            return http.JsonResponse({'code': 4013, 'errmsg': '错误的sku_id'})

        rsp = http.JsonResponse({'code': 0, 'errmsg': '删除购物车成功'})

        # 判断用户是否登录
        user = request.user
        if user and user.is_authenticated:
            # 登录用户，修改redis数据库
            self.set_redis()
            return rsp
        else:
            # 未登录用户，修改cookies
            return self.set_cookies(rsp)


class CartManager2(CartManager):
    """
    购物车管理2
    """

    def put(self, request):
        """
        购物车商品全选
        :param request: put /carts/selection  body:json:{selected:bool}是否全选
        :return: json: {code:int, errmsg:str}
        """
        # 解析json数据
        self.parse(request)
        # 校验数据
        if not isinstance(self.selected, bool):
            return http.JsonResponse({'code': 4014, 'errmsg': '错误的selected'})

        rsp = http.JsonResponse({'code': 0, 'errmsg': '购物车全选成功'})

        # 判断用户是否登录
        user = request.user
        if user and user.is_authenticated:
            # 登录用户，修改redis数据库
            # 连接redis
            redis_conn = get_redis_connection('cart')
            # 获取所有商品，数据结构：
            # hash增量字典 cart_id:{sku_id: count, ...}
            cart = redis_conn.hgetall('cart_%s' % user.id)
            # 保存所有商品到list列表,保存已勾选商品 selected_id:[sku_id,...]
            for sku_id in cart:
                if self.selected:
                    redis_conn.sadd('selected_%s' % user.id, sku_id)
                else:
                    redis_conn.srem('selected_%s' % user.id, sku_id)
        else:
            # 未登录用户，修改cookies
            # 获取cookie原购物车数据
            cart = self.request.COOKIES.get('cart')

            # 解密
            cart = decrypt(cart)
            if not isinstance(cart, dict):
                cart = {}

            # 修改所有商品的selected
            for sku in cart.values():
                sku['selected'] = self.selected
            # 数据结构：
            # cart:{sku_id:{count:int, selected:bool},...}
            # 加密
            cart = encrypt(cart)
            # 设置响应cookies
            rsp.set_cookie('cart', cart)

        return rsp

    def get(self, request):
        """
        非购物车页面显示购物车数据
        :param request: GET /carts/simple/
        :return: json {'code': 0, 'errmsg': '获取购物车成功', 'cart_skus': cart_skus}
        """
        user = request.user
        if user.is_authenticated:
            redis_conn = get_redis_connection('cart')
            cart = redis_conn.hgetall('cart_%s' % user.id)
            data = {}
            for sku, count in cart.items():
                data[int(sku)] = {
                    'count': int(count),
                }
        else:
            data = request.COOKIES.get('cart')
            data = decrypt(data)
            if not isinstance(data, dict):
                data = {}

        cart_skus = []
        if data:
            sku_ids = data.keys()
            skus = SKU.objects.filter(id__in=sku_ids)

            for sku in skus:
                cart_skus.append({
                    'id': sku.id,
                    'count': data.get(sku.id).get('count'),
                    'name': sku.name,
                    'default_image_url': sku.default_image.url,
                })

        return http.JsonResponse({'code': 0, 'errmsg': '获取购物车成功', 'cart_skus': cart_skus})

class CartManager3(CartManager):
    """
     购物车管理3
    """
    def get(self, request):
        """
        登录用户购物车页面显示cookies购物车数据
        :param request: GET /carts/cookie/
        :return: json {'code': 0, 'errmsg': '获取购物车成功', 'cart_skus': cart_skus}
        """
        data = request.COOKIES.get('cart')
        data = decrypt(data)
        if not isinstance(data, dict):
            data = {}

        cart_skus = []
        if data:
            sku_ids = data.keys()
            skus = SKU.objects.filter(id__in=sku_ids)

            for sku in skus:
                cart_skus.append({
                    'id': sku.id,
                    'count': data.get(sku.id).get('count'),
                    'name': sku.name,
                    'default_image_url': sku.default_image.url,
                })

        return http.JsonResponse({'code': 0, 'errmsg': '获取购物车成功', 'cart_skus': cart_skus})


class CartManager4(CartManager):
    """
     购物车管理3
    """
    def get(self, request):
        """
        同步登录前的购物车
        :param request: /carts/merge/
        :return: 刷新或重定向 /carts/
        """
        # 重定向到购物车页面，刷新
        rsp = redirect(reverse('cart:cart'))
        # 验证用户
        user = request.user
        if not user or not user.is_authenticated:
          # print('未登录用户')
            return rsp
        # 获取并验证cookie原购物车数据
        cart = self.request.COOKIES.get('cart')
        if not cart:
          # print('cookie购物车为空')
            return rsp
        # 解密
        cookie_cart = decrypt(cart)

        # 使用合法的数据结构装载redis中要修改的数据，以便修改redis中的数据
        redis_cart_mset = {}
        redis_selected_add = []
        redis_selected_rem = []
        # 遍历出cookies中的购物车数据
        for id, sku in cookie_cart.items():
            redis_cart_mset[str(id)] = sku['count']
            if sku['selected']:
                redis_selected_add.append(id)
            else:
                redis_selected_rem.append(id)

        redis_conn = get_redis_connection('cart')
        # 修改redis中的数据
        pl = redis_conn.pipeline()
      # print(redis_cart_mset)
        pl.hmset('cart_%s' % user.id, redis_cart_mset)
        if redis_selected_add:
            pl.sadd('selected_%s' % user.id, *redis_selected_add)
        if redis_selected_rem:
            pl.srem('selected_%s' % user.id, *redis_selected_rem)
        pl.execute()
      # print('已更新redis购物车')
        # 删除cookies
        rsp.delete_cookie('cart')
        return rsp